home *** CD-ROM | disk | FTP | other *** search
- /* SMTP Server state machine - see RFC 821
- * enhanced 4/88 Dave Trulli nn2z
- */
-
- /****************************************************************************
- * $Id: smtpserv.c 1.13 94/01/04 14:10:28 ROOT_DOS Exp $
- *
- * 25 Jun 92 paul@wolf.demon.co.uk added automated mail bouncing
- * 13 Jul 92 1.3 GT Remove 1.2 changes.
- * 27 Aug 92 1.6 mt@kram.demon.co.uk added smtp separator
- * 03 Sep 92 1.7 GT Fix missing BOUNCER conditional.
- * 08 Dec 92 1.8 mt@kram.org: Occasional pwait() during copy
- * cms@home: Alias-File Mail Bouncing System
- * 19 Mar 93 1.9 GT Debugging mailit.
- * 04 Apr 93 1.10 GT Re-enable beep.
- * 05 Apr 93 1.11 GT Reinstate delay call.
- * 08 May 93 1.12 GT Fix warnings.
- * IAY Improve behaviour of rest of system
- * while this module is copying files,
- * particularly while in SMTP MODE QUEUE.
- * IAY Fix dot transparency as per RFC 821.
- * GBD Fix alias expansion to allow multiple spaces.
- * 08 Dec 93 1.13 GT Fix bounce message envelope From.
- * Version and compilation date in SMTP banner.
- * Implement VRFY.
- * Fix wandering pointer in getmsgtxt ().
- * Close mail spool file before ACK.
- * Fix "." at column 256.
- *
- * ATARI Version by David Nash - dnash@chaos.demon.co.uk
- *
- * __stdargs smtpserv
- *
- ****************************************************************************/
-
- /*
- ** The following definition controls how often the copy_data
- ** function calls pwait(). Lower numbers mean better response
- ** to other things that may be going on at the expense of the
- ** speed of that particular function.
- */
- #define COPY_DATA_WAIT 1
-
- #include <stdio.h>
- #include <time.h>
-
- #undef DEBUG_LOCKS
-
- #ifdef NOVELL
-
- #include "nit.h"
- #include <io.h>
- #include <dos.h>
- #include <dir.h>
- #include <time.h>
-
- #endif
-
- extern char novell_server_name[128];
- extern char novell_mail_ext[4];
- extern unsigned short novell_start;
- extern char *smtp_separator;
- extern int Smtpbeep;
-
- #ifdef UNIX
- #include <sys/types.h>
- #endif
- #if defined(__STDC__) || defined(__TURBOC__)
- #include <stdarg.h>
- #endif
- #include <ctype.h>
- #include <setjmp.h>
- #include "global.h"
- #include "mbuf.h"
- #include "cmdparse.h"
- #include "socket.h"
- #include "iface.h"
- #include "proc.h"
- #include "smtp.h"
- #include "commands.h"
- #include "dirutil.h"
- #include "bm.h"
- #include "mailbox.h"
- #include "domain.h"
- #include "files.h"
- #include "ip.h"
-
- char *Days[7] =
- {
- "Sun","Mon","Tue","Wed","Thu","Fri","Sat"
- }
- ;
- char *Months[12] =
- {
- "Jan","Feb","Mar","Apr","May","Jun",
- "Jul","Aug","Sep","Oct","Nov","Dec"
- }
- ;
-
-
-
- static void copy_data __ARGS((FILE *from, FILE *to));
- static struct list *expandalias __ARGS((struct list **head,char *user));
- static int getmsgtxt __ARGS((struct smtpsv *mp));
- static struct smtpsv *mail_create __ARGS((void));
- static void mail_clean __ARGS((struct smtpsv *mp));
- static int mailit __ARGS((FILE *data,char *from,struct list *tolist));
- static int router_queue __ARGS((FILE *data,char *from,struct list *to));
- static void smtplog __ARGS((char *fmt,...));
- static void __stdargs smtpserv __ARGS((int s,void *unused,void *p));
- static int mailuser __ARGS((FILE *data,char *from,char *to));
- static int validate_user __ARGS((char *user));
- static int validate_sender __ARGS((char *sender));
- static int remlist __ARGS((struct list **head, struct list *unwanted));
-
- /* Command table */
- static char *commands[] =
- {
- "helo",
- #define HELO_CMD 0
- "noop",
- #define NOOP_CMD 1
- "mail from:",
- #define MAIL_CMD 2
- "quit",
- #define QUIT_CMD 3
- "rcpt to:",
- #define RCPT_CMD 4
- "help",
- #define HELP_CMD 5
- "data",
- #define DATA_CMD 6
- "rset",
- #define RSET_CMD 7
- "expn",
- #define EXPN_CMD 8
- "vrfy",
- #define VRFY_CMD 9
- NULLCHAR
- }
- ;
-
- /* Reply messages */
- static char Help[] = "214-Commands:\n214-HELO NOOP MAIL QUIT RCPT HELP DATA RSET EXPN\n214 End\n";
- static char Banner[] = "220 %s %s %s %s SMTP ready\n";
- static char Closing[] = "221 Closing\n";
- static char Ok[] = "250 Ok\n";
- static char Reset[] = "250 Reset state\n";
- static char Sent[] = "250 Sent\n";
- static char Ourname[] = "250 %s, Pleased to meet you\n";
- static char Unwanted[] = "250 Local policy blocks mail from <%s>\n";
- static char Enter[] = "354 Enter mail, end with .\n";
- static char Ioerr[] = "452 Temp file write error\n";
- static char Badcmd[] = "500 Command unrecognized\n";
- static char Lowmem[] = "421 System overloaded, try again later\n";
- static char Syntax[] = "501 Syntax error\n";
- static char Needrcpt[] = "503 Need RCPT (recipient)\n";
- static char Needsender[] = "503 Local policy blocking mail from you\n";
- static char Unknown[] = "550 <%s> address unknown\n";
- static char Noalias[] = "550 No alias for <%s>\n";
- static char UnknownRcpt[] = "550 Unknown recipient <%s>\n";
-
- static failure fail;
- static int bad_sender;
- char default_address[SLINELEN];
-
- static int Ssmtp = -1; /* prototype socket for service */
-
- /* Start up SMTP receiver service */
- int
- smtp1(argc,argv,p)
- int argc;
- char *argv[];
- void *p;
- {
- struct sockaddr_in lsocket;
- int s;
-
- if(Ssmtp != -1)
- {
- return 0;
- }
- psignal(Curproc,0); /* Don't keep the parser waiting */
- chname(Curproc,"SMTP listener");
-
- lsocket.sin_family = AF_INET;
- lsocket.sin_addr.s_addr = INADDR_ANY;
- if(argc < 2)
- lsocket.sin_port = IPPORT_SMTP;
- else
- lsocket.sin_port = atoi(argv[1]);
-
- Ssmtp = socket(AF_INET,SOCK_STREAM,0);
- bind(Ssmtp,(char *)&lsocket,sizeof(lsocket));
- listen(Ssmtp,1);
- for(;;)
- {
- if((s = accept(Ssmtp,NULLCHAR,(int *)NULL)) == -1)
- break; /* Service is shutting down */
-
- if(availmem() < Memthresh)
- {
- usprintf(s,Lowmem);
- shutdown(s,1);
- }
- else
- {
- /* Spawn a server */
- newproc("SMTP server",2048,smtpserv,s,NULL,NULL,0);
- }
- }
- return 0;
- }
-
- /* Shutdown SMTP service (existing connections are allowed to finish) */
- int
- smtp0(argc,argv,p)
- int argc;
- char *argv[];
- void *p;
- {
- close_s(Ssmtp);
- Ssmtp = -1;
- return 0;
- }
-
- static void __stdargs smtpserv(s,unused,p)
- int s;
- void *unused;
- void *p;
- {
- struct smtpsv *mp;
- char **cmdp,buf[LINELEN],*arg,*cp,*cmd,*newaddr;
- struct list *ap,*list;
- int cnt;
- char address_type;
-
- sockmode(s,SOCK_ASCII);
- sockowner(s,Curproc); /* We own it now */
- log(s,"open SMTP");
-
- if((mp = mail_create()) == NULLSMTPSV)
- {
- tprintf(Nospace);
- log(s,"close SMTP - no space");
- close_s(s);
- return;
- }
- mp->s = s;
-
- (void) usprintf(s, Banner, Hostname, Version, __DATE__, __TIME__);
-
- loop:
- if ((cnt = recvline(s,buf,sizeof(buf))) == -1)
- {
- /* He closed on us */
- goto quit;
- }
- if(cnt < 4)
- {
- /* Can't be a legal command */
- usprintf(mp->s,Badcmd);
- goto loop;
- }
- rip(buf);
- cmd = buf;
-
- /* Translate entire buffer to lower case */
- for(cp = cmd;*cp != '\0';cp++)
- *cp = tolower(*cp);
-
- /* Find command in table; if not present, return syntax error */
- for(cmdp = commands;*cmdp != NULLCHAR;cmdp++)
- if(strncmp(*cmdp,cmd,strlen(*cmdp)) == 0)
- break;
- if(*cmdp == NULLCHAR)
- {
- (void) usprintf(mp->s,Badcmd);
- goto loop;
- }
- arg = &cmd[strlen(*cmdp)];
- /* Skip spaces after command */
- while(*arg == ' ')
- arg++;
-
- /* Execute specific command */
-
- switch(cmdp-commands) {
- case HELO_CMD:
- free(mp->system);
- mp->system = strdup(arg);
- (void) usprintf(mp->s,Ourname,Hostname);
- break;
- case NOOP_CMD:
- (void) usprintf(mp->s,Ok);
- break;
- case MAIL_CMD:
- bad_sender = 0;
- if((cp = getname(arg)) == NULLCHAR)
- {
- (void) usprintf(mp->s,Syntax);
- break;
- }
- if (!validate_sender(cp))
- {
- (void) usprintf(mp->s,Unwanted,cp);
- smtplog("rejected: from: %s",cp);
- bad_sender = 1;
- break;
- }
- free(mp->from);
- mp->from = strdup(cp);
- (void) usprintf(mp->s,Ok);
- break;
- case QUIT_CMD:
- (void) usprintf(mp->s,Closing);
- goto quit;
- case RCPT_CMD:
- /* Specify recipient */
- fail = NO_FAIL;
- if((cp = getname(arg)) == NULLCHAR)
- {
- (void) usprintf(mp->s,Syntax);
- break;
- }
-
- /* rewrite address if possible */
- if((newaddr = rewrite_address(cp)) != NULLCHAR)
- {
- strcpy(buf,newaddr);
- cp = buf;
- free(newaddr);
- }
-
- /* check if address is ok */
- if ((address_type = validate_address(cp)) == BADADDR)
- {
- (void) usprintf(mp->s,Unknown,cp);
- break;
- }
-
- /* if a local address check for an alias */
- if (address_type == LOCAL)
- {
- expandalias(&mp->to, cp);
- for (ap = mp->to; ap != NULLLIST; ap = ap->next)
- {
- if (ap->type == LOCAL)
- {
- if (!validate_user(ap->val))
- {
- switch (fail)
- {
- case FAIL_DELIVER:
- break;
- case FAIL_DEFAULT:
- expandalias(&ap->next, default_address);
- if (validate_user(ap->next->val))
- {
- remlist(&mp->to, ap);
- break;
- }
- /* else fall through to fail */
- default:
- usprintf(mp->s,UnknownRcpt,ap->val);
- fail = FAIL_BAD;
- break;
- }
- if (fail == FAIL_BAD)
- break;
- }
- }
- }
- if (fail == FAIL_BAD)
- break;
-
- }
- else
- /* a remote address is added to the list */
- addlist(&mp->to, cp, address_type);
-
- (void) usprintf(mp->s,Ok);
- break;
- case HELP_CMD:
- (void) usprintf(mp->s,Help);
- break;
- case DATA_CMD:
- if ((mp->to == NULLLIST) || fail == FAIL_BAD)
- (void) usprintf(mp->s,Needrcpt);
- else if (bad_sender)
- (void) usprintf(mp->s,Needsender);
- else if ((mp->data = tmpfile()) == NULLFILE)
- (void) usprintf(mp->s,Ioerr);
- else
- getmsgtxt(mp);
- break;
- case RSET_CMD:
- del_list(mp->to);
- mp->to = NULLLIST;
- fail = NO_FAIL;
- (void) usprintf(mp->s,Reset);
- break;
- case EXPN_CMD:
- if (*arg == '\0')
- {
- (void) usprintf(mp->s,Syntax);
- break;
- }
-
- list = NULLLIST;
- /* rewrite address if possible */
- if((newaddr = rewrite_address(arg)) != NULLCHAR)
- if(strcmp(newaddr,arg) == 0)
- {
- free(newaddr);
- newaddr = NULLCHAR;
- }
- else
- {
- strcpy(buf,newaddr);
- arg = buf;
- }
- list = NULLLIST;
- expandalias(&list,arg);
- if (strcmp(list->val,arg) == 0 && list->next == NULLLIST)
- if(newaddr == NULLCHAR)
- {
- (void) usprintf(mp->s,Noalias,arg);
- del_list(list);
- break;
- }
- ap = list;
- while (ap->next != NULLLIST)
- {
- (void) usprintf(mp->s,"250-%s\n",ap->val);
- ap = ap->next;
- }
- usprintf(mp->s,"250 %s\n",ap->val);
- del_list(list);
- free(newaddr);
- break;
-
- case VRFY_CMD:
- if (*arg == '\0') {
- usprintf(mp->s, Syntax);
- break;
- }
-
- if (validate_user(arg) == 0)
- usprintf(mp->s, UnknownRcpt, arg);
- else
- usprintf(mp->s, Ok);
-
- break;
-
- }
- goto loop;
-
- quit:
- log(mp->s,"close SMTP");
- close_s(mp->s);
- mail_clean(mp);
- smtptick(NULL); /* start SMTP daemon immediately */
- }
-
- /* read the message text */
- static int
- getmsgtxt(mp)
- struct smtpsv *mp;
- {
- char buf[LINELEN];
- register char *p = buf;
- long t;
-
- /* Add timestamp; ptime adds newline */
-
- time(&t);
- fprintf(mp->data,"Received: ");
-
- if (mp->system != NULLCHAR)
- fprintf(mp->data, "from %s ", mp->system);
- fprintf(mp->data, "by %s with SMTP\n\tid AA%ld ; %s",
- Hostname, get_msgid(), ptime(&t));
-
- if (ferror(mp->data)) {
- usprintf(mp->s, Ioerr);
- return 1;
- }
- else
- usprintf(mp->s, Enter);
-
- while(1) {
- int no_check_dot = 0; /* don't check for "." */
- char *has_newline; /* nz - line contains newline */
-
- p = buf; /* GT 11 Dec 93 */
-
- if (recvline(mp->s, p, sizeof(buf)) == -1)
- return 1;
-
- has_newline = strchr(p, '\n');
- rip(p);
-
- /*
- ** If the line starts with a '.', this is either
- ** the end of the message or a line where a dot
- ** has been added for "dot transparency"
- */
-
- if (no_check_dot == 0 && *p == '.') {
- /*
- ** Strip off leading '.'; if there is nothing
- ** else on the line, this is the end of the
- ** data. Otherwise, we have managed to remove
- ** the protecting dot and p now points at the
- ** unprotected message line.
- */
-
- if (*++p == '\0') {
- int rc; /* result code */
-
- /* Also sends appropriate response */
-
- rc = mailit(mp->data, mp->from, mp->to);
- if (fclose(mp->data) != 0)
- rc = 1;
-
- mp->data = NULLFILE;
-
- if (rc != 0)
- (void) usprintf(mp->s,Ioerr);
- else
- (void) usprintf(mp->s,Sent);
-
- del_list(mp->to);
- mp->to = NULLLIST;
- return 0;
- }
-
- }
-
- if (has_newline != 0)
- no_check_dot = 0; /* check "." next time */
- else
- no_check_dot = 1; /* don't check "." next time */
-
- /* for UNIX mail compatiblity */
-
- if (strncmp(p,"From ",5) == 0)
- (void) putc('>',mp->data);
-
- /* Append to data file */
-
- if(fprintf(mp->data,"%s\n",p) < 0) {
- (void) usprintf(mp->s,Ioerr);
- return 1;
- }
- }
- }
-
- /* Create control block, initialize */
- static struct smtpsv *
- mail_create()
- {
- register struct smtpsv *mp;
-
- mp = (struct smtpsv *)callocw(1,sizeof(struct smtpsv));
- mp->from = strdup(""); /* Default to null From address */
- return mp;
- }
-
- /* Free resources, delete control block */
- static void
- mail_clean(mp)
- register struct smtpsv *mp;
- {
- if (mp == NULLSMTPSV)
- return;
- free(mp->system);
- free(mp->from);
- if(mp->data != NULLFILE)
- fclose(mp->data);
- del_list(mp->to);
- free((char *)mp);
- }
-
-
- /* Given a string of the form <user@host>, extract the part inside the
- * brackets and return a pointer to it.
- */
- char *
- getname(cp)
- register char *cp;
- {
- register char *cp1;
-
- if ((cp = strchr(cp,'<')) == NULLCHAR)
- return NULLCHAR;
- cp++; /* cp -> first char of name */
- if ((cp1 = strchr(cp,'>')) == NULLCHAR)
- return NULLCHAR;
- *cp1 = '\0';
- return cp;
- }
-
- #ifdef NOVELL
-
- long int get_mail_id(char *user,char *server)
- {
- int connectionID=1;
- int cCode=0;
- int DefCon;
- long int retval;
- int err;
-
- cCode=GetConnectionID(server,&connectionID);
-
- DefCon=GetDefaultConnectionID();
-
- if(DefCon!=connectionID)
- {
- SetPreferredConnectionID(connectionID);
- }
-
- cCode=GetBinderyObjectID(user,OT_USER,&retval);
-
- if(cCode!=0)
- {
- retval=0;
- }
-
- if(DefCon!=connectionID)
- {
- SetPrimaryConnectionID(DefCon);
- }
- return retval;
- }
-
- void send_alert(char *user,char *server,char *from)
- {
- int numcon,conlist[100],reslist[100];
- int connectionID=1;
- int cCode=0;
- int DefCon;
- long int retval;
- int err;
- char message[128];
-
- cCode=GetConnectionID(server,&connectionID);
-
- DefCon=GetDefaultConnectionID();
-
- if(DefCon!=connectionID)
- {
- SetPreferredConnectionID(connectionID);
- }
-
- cCode=GetObjectConnectionNumbers(user,OT_USER,&numcon,conlist,100);
-
- sprintf(message,"You have mail from %s",from);
-
- message[55]=0; /* Nasty truncation to cope with bindings */
-
- cCode=SendBroadcastMessage(message,conlist,reslist,numcon);
-
- if(DefCon!=connectionID)
- {
- SetPrimaryConnectionID(DefCon);
- }
- }
-
- #endif
-
- /*
- ** copy_data
- **
- ** This function copies all available data from an input file to an
- ** output file. This is done in chunks of LINELEN for efficiency.
- ** In addition, the function deschedules itself every so often so
- ** that large copy operations of this type do not completely block
- ** processing of other operations.
- */
- static void
- copy_data ( from, to )
- FILE *from, *to;
- {
- int c;
- #if COPY_DATA_WAIT != 1
- int counter = 0;
- #endif
- char buf[LINELEN];
- while ( (c = fread(buf, 1, sizeof(buf), from) ) > 0)
- {
- if (fwrite(buf, 1, c, to) != c)
- break;
- #if COPY_DATA_WAIT == 1
- pwait(NULL);
- #else
- if (++counter == COPY_DATA_WAIT)
- {
- pwait(NULL);
- counter = 0;
- }
- #endif
- }
- }
-
- /* General mailit function. It takes a list of addresses which have already
- ** been verified and expanded for aliases. Base on the current mode the message
- ** is place in an mbox, the outbound smtp queue or the rqueue interface
- */
- static int
- mailit(data,from,tolist)
- FILE *data;
- char *from;
- struct list *tolist;
- {
- struct list *ap, *dlist = NULLLIST;
- register FILE *fp;
- char mailbox[85], *cp, *host, *qhost;
- int fail = 0;
- time_t t;
-
- if ((Smtpmode & QUEUE) != 0)
- return(router_queue(data,from,tolist));
-
- do
- {
- qhost = NULLCHAR;
- for(ap = tolist;ap != NULLLIST;ap = ap->next)
- {
- #if defined (DEBUG_LOCKS)
- smtplog ("mailit (): ap->val = %s", ap->val);
- smtplog ("mailit (): ap->type = %d", ap->type);
- #endif
- if (ap->type == DOMAIN)
- {
- if ((host = strrchr(ap->val,'@')) != NULLCHAR)
- host++;
- else
- host = Hostname;
- if(qhost == NULLCHAR)
- qhost = host;
- if(stricmp(qhost,host) == 0)
- {
- ap->type = BADADDR;
- addlist(&dlist,ap->val,0);
- }
- }
- }
- if(qhost != NULLCHAR)
- {
- #if defined (DEBUG_LOCKS)
- smtplog ("mailit (): qhost = %s, queueing", qhost);
- #endif
- rewind(data);
- queuejob(data,qhost,dlist,from,NULL);
- del_list(dlist);
- dlist = NULLLIST;
- }
- }
- while(qhost != NULLCHAR)
- ;
-
- for(ap = tolist;ap != NULLLIST;ap = ap->next)
- {
- if(ap->type != LOCAL)
- {
- ap->type = DOMAIN;
- continue;
- }
- rewind(data);
- /* strip off host name of LOCAL addresses */
- if ((cp = strchr(ap->val,'@')) != NULLCHAR)
- *cp = '\0';
-
- /* truncate long user names */
- if (strlen(ap->val) > MBOXLEN)
- ap->val[MBOXLEN] = '\0';
-
- /* if mail file is busy save it in our smtp queue
- * and let the smtp daemon try later.
- */
-
- #ifdef NOVELL
- if(!novell_start)
- {
- #endif
- if (mlock(Mailspool,ap->val))
- {
- int32 msgid;
-
- #if defined (DEBUG_LOCKS)
- smtplog ("mailit (): mail spool file %s, %s locked, queueing",
- Mailspool, ap->val);
- #endif
- addlist(&dlist,ap->val,0);
- fail = queuejob(data,Hostname,dlist,from,&msgid);
- if (!fail)
- delay_job(msgid);
-
- del_list(dlist);
- dlist = NULLLIST;
- }
- #ifdef NOVELL
- }
- #endif
- else
- {
- char buf[LINELEN];
- int tocnt = 0;
- extern int smtpverbose;
-
- #ifdef NOVELL
- long int oid;
- char box[13];
- struct ffblk fblock;
- static int done_rand=0;
-
- if(novell_start)
- {
- oid=get_mail_id(ap->val,novell_server_name);
-
- ltoa(oid,box,16);
-
- if(!done_rand)
- {
- randomize(); /* This is non-deterministic */
- done_rand=1;
- }
-
- do
- {
- sprintf(mailbox,"%s/%s/%s/%04x%04x.%s",novell_server_name,"SYS:/MAIL",box,rand(),rand(),novell_mail_ext);
- }
- while(!findfirst(mailbox,&fblock,0))
- ;
- }
- else
- #endif
- {
- sprintf(mailbox,"%s/%s.txt",Mailspool,ap->val);
- }
-
- #ifndef AMIGA
- if((fp = fopen(mailbox,APPEND_TEXT)) != NULLFILE)
- {
- # ifdef NOVELL
- if(novell_start)
- {
- send_alert(ap->val,novell_server_name,from);
- }
- # endif
-
- #else
- if((fp = fopen(mailbox,"r+")) != NULLFILE)
- {
- (void) fseek(fp, 0L, 2);
- #endif
- time(&t);
- if (smtp_separator)
- fprintf(fp, "%s\n", smtp_separator);
- fprintf(fp,"From %s %s",from,ctime(&t));
- host = NULLCHAR;
- while(fgets(buf,sizeof(buf),data) != NULLCHAR)
- {
- if(buf[0] == '\n')
- {
- if(tocnt == 0)
- fprintf(fp,"%s%s\n",
- Hdrs[APPARTO],
- ap->val);
- fputc('\n',fp);
- break;
- }
- fputs(buf,fp);
- rip(buf);
- switch(htype(buf))
- {
- case TO:
- case CC:
- ++tocnt;
- break;
- case RRECEIPT:
- if((cp = getaddress(buf,0))
- != NULLCHAR)
- {
- free(host);
- host = strdup(cp);
- }
- break;
- }
- }
- copy_data(data, fp);
- if(ferror(fp))
- fail = 1;
- else
- fprintf(fp,"\n");
- /* Leave a blank line between msgs */
- fclose(fp);
-
- if (smtpverbose)
- {
- tprintf("New mail arrived for %s from %s\n",ap->val, from);
- }
-
- if (Smtpbeep)
- printf("\a\a");
-
- if(host != NULLCHAR)
- {
- rewind(data); /* Send return receipt */
- mdaemon(data,host,NULLLIST,0);
- free(host);
- }
- }
- else
- fail = 1;
- #ifdef NOVELL
- if(!novell_start)
- {
- (void) rmlock(Mailspool,ap->val);
- }
- #else
- (void) rmlock(Mailspool,ap->val);
- #endif
- if (fail)
- {
- break;
- }
- smtplog("deliver: To: %s From: %s",ap->val,from);
- }
- }
- return fail;
- }
-
- /* Return Date/Time in Arpanet format in passed string */
- char *
- ptime(t)
- long *t;
- {
- /* Print out the time and date field as
- * "DAY day MONTH year hh:mm:ss ZONE"
- */
- register struct tm *ltm;
- static char tz[4];
- static char str[40];
- char *p;
- #ifndef ATARI
- *getenv();
- #endif
- /* Read the system time */
-
- ltm = localtime(t);
-
- if (*tz == '\0') {
- if ((p = getenv("TZ")) == NULL)
- strcpy(tz, "UTC");
- else {
- if (ltm->tm_isdst == 0)
- strncpy(tz, p, 3);
- else
- strncpy(tz, p + 4, 3);
- }
- }
-
- /* rfc 822 format */
-
- sprintf(str,"%s, %.2d %s %02d %02d:%02d:%02d %.3s\n",
- Days[ltm->tm_wday],
- ltm->tm_mday,
- Months[ltm->tm_mon],
- ltm->tm_year,
- ltm->tm_hour,
- ltm->tm_min,
- ltm->tm_sec,
- tz);
- return(str);
- }
-
- long
- get_msgid()
- {
- char sfilename[LINELEN];
- char s[20];
- register long sequence = 0;
- FILE *sfile;
-
- sprintf(sfilename,"%s/sequence.seq",Mailqdir);
- sfile = fopen(sfilename,READ_TEXT);
-
- /* if sequence file exists, get the value, otherwise set it */
- if (sfile != NULL)
- {
- (void) fgets(s,sizeof(s),sfile);
- sequence = atol(s);
- /* Keep it in range of and 8 digit number to use for dos name prefix. */
- if (sequence < 0L || sequence > 99999999L )
- sequence = 0;
- fclose(sfile);
- }
-
- /* increment sequence number, and write to sequence file */
- sfile = fopen(sfilename,WRITE_TEXT);
- fprintf(sfile,"%ld",++sequence);
- fclose(sfile);
- return sequence;
- }
-
- #if defined(MSDOS) || defined(ATARI)
- /* Illegal characters in a DOS filename */
- static char baddoschars[] = "\"[]:|<>+=;,";
- #endif
-
- /* test if mail address is valid */
- int
- validate_address(s)
- char *s;
- {
- char *cp, *t;
- int32 addr;
-
- /* if address has @ in it the check dest address */
- if (*s == '@' && (cp = strrchr(s,':')) != NULLCHAR)
- for (t = s,cp++; (*t++ = *cp++) != 0; )
- ;
- if ((cp = strrchr(s,'@')) != NULLCHAR)
- {
- cp++;
- /* 1st check if its our hostname
- * if not then check the hosts file and see
- * if we can resolve ther address to a know site
- * or one of our aliases
- */
- if (strcmp(cp,Hostname) != 0)
- {
- if ((addr = mailroute(cp, 0)) == 0
- && (Smtpmode & QUEUE) == 0)
- return BADADDR;
- if (ismyaddr(addr) == NULLIF)
- return DOMAIN;
- }
-
- /* on a local address remove the host name part */
- *--cp = '\0';
- }
-
- /* if using an external router leave address alone */
- if ((Smtpmode & QUEUE) != 0)
- return LOCAL;
-
- /* check for the user%host hack */
- if ((cp = strrchr(s,'%')) != NULLCHAR)
- {
- *cp = '@';
- cp++;
- /* reroute based on host name following the % seperator */
- if (mailroute(cp, 0) == 0)
- return BADADDR;
- else
- return DOMAIN;
- }
-
- #if defined (MSDOS) || defined(ATARI) /* dos file name checks */
- /* Check for characters illegal in MS-DOS file names */
- for(cp = baddoschars;*cp != '\0';cp++)
- {
- if(strchr(s,*cp) != NULLCHAR)
- return BADADDR;
- }
- #endif
- return LOCAL;
- }
-
- /* place a mail job in the outbound queue */
- int
- queuejob(dfile,host,to,from,msgid)
- FILE *dfile;
- char *host;
- struct list *to;
- char *from;
- int32 *msgid;
- {
- FILE *fp;
- struct list *ap;
- char tmpstring[50], prefix[9], buf[LINELEN];
- register int cnt;
- int32 this_msgid;
-
- this_msgid = get_msgid();
- sprintf(prefix,"%ld",this_msgid);
- if (msgid)
- *msgid = this_msgid;
- mlock(Mailqdir,prefix);
- sprintf(tmpstring,"%s/%s.txt",Mailqdir,prefix);
- if((fp = fopen(tmpstring,WRITE_TEXT)) == NULLFILE)
- {
- (void) rmlock(Mailqdir,prefix);
- return 1;
- }
- while((cnt = fread(buf, 1, LINELEN, dfile)) > 0)
- if(fwrite(buf, 1, cnt, fp) != cnt)
- break;
- if(ferror(fp))
- {
- fclose(fp);
- (void) rmlock(Mailqdir,prefix);
- return 1;
- }
- fclose(fp);
- sprintf(tmpstring,"%s/%s.wrk",Mailqdir,prefix);
- if((fp = fopen(tmpstring,WRITE_TEXT)) == NULLFILE)
- {
- (void) rmlock(Mailqdir,prefix);
- return 1;
- }
- fprintf(fp,"%s\n%s\n",host,from);
- for(ap = to; ap != NULLLIST; ap = ap->next)
- {
- fprintf(fp,"%s\n",ap->val);
- smtplog("queue job %s To: %s From: %s",prefix,ap->val,from);
- }
- fclose(fp);
- (void) rmlock(Mailqdir,prefix);
- return 0;
- }
-
- /* Deliver mail to the appropriate mail boxes */
- static int
- router_queue(data,from,to)
- FILE *data;
- char *from;
- struct list *to;
- {
- register struct list *ap;
- FILE *fp;
- char tmpstring[50];
- char prefix[9];
-
- sprintf(prefix,"%ld",get_msgid());
- mlock(Routeqdir,prefix);
- sprintf(tmpstring,"%s/%s.txt",Routeqdir,prefix);
- if((fp = fopen(tmpstring,WRITE_TEXT)) == NULLFILE)
- {
- (void) rmlock(Routeqdir,prefix);
- return 1;
- }
- rewind(data);
- copy_data(data, fp);
- if(ferror(fp))
- {
- fclose(fp);
- (void) rmlock(Routeqdir,prefix);
- return 1;
- }
- fclose(fp);
- sprintf(tmpstring,"%s/%s.wrk",Routeqdir,prefix);
- if((fp = fopen(tmpstring,WRITE_TEXT)) == NULLFILE)
- {
- (void) rmlock(Routeqdir,prefix);
- return 1;
- }
- fprintf(fp,"From: %s\n",from);
- for(ap = to;ap != NULLLIST;ap = ap->next)
- {
- fprintf(fp,"To: %s\n",ap->val);
- }
- fclose(fp);
- (void) rmlock(Routeqdir,prefix);
- smtplog("rqueue job %s From: %s",prefix,from);
- return 0;
- }
-
- /* add an element to the front of the list pointed to by head
- ** return NULLLIST if out of memory.
- */
- struct list *
- addlist(head,val,type)
- struct list **head;
- char *val;
- int type;
- {
- register struct list *tp;
-
- tp = (struct list *)callocw(1,sizeof(struct list));
-
- tp->next = NULLLIST;
-
- /* allocate storage for the char string */
- tp->val = strdup(val);
- tp->type = type;
-
- /* add entry to front of existing list */
- if (*head == NULLLIST)
- *head = tp;
- else
- {
- tp->next = *head;
- *head = tp;
- }
- return tp;
-
- }
-
- /* Remove and entry from the list ... CMS 19/12/92
- return TRUE on success or FALSE on failure */
- static int remlist(struct list **head, struct list *unwanted)
- {
- struct list *ptr, **prev;
- prev = head;
- for (ptr = *head; ptr; ptr = ptr->next)
- if (ptr == unwanted)
- {
- *prev = ptr->next;
- free (ptr);
- break;
- }
- else prev = &ptr->next;
- return ptr == unwanted;
- }
-
- #define SKIPWORD(X) while(*X && *X!=' ' && *X!='\t' && *X!='\n' && *X!= ',') X++;
- #define SKIPSPACE(X) while(*X ==' ' || *X =='\t' || *X =='\n' || *X == ',') X++;
-
- /* check for and alias and expand alias into a address list */
- static struct list *
- expandalias(head, user)
- struct list **head;
- char *user;
- {
- FILE *fp;
- register char *s,*p;
- struct rr *rrp, *rrlp;
- int inalias = 0;
- struct list *tp;
- char buf[LINELEN];
-
- /* no alias file found */
- if ((fp = fopen(Alias, READ_TEXT)) == NULLFILE)
- {
- fail = FAIL_DELIVER;
- /* Try MB, MG or MR domain name records */
- rrlp = rrp = resolve_mailb(user);
- while(rrp != NULLRR)
- {
- if(rrp->rdlength > 0)
- {
- /* remove the trailing dot */
- rrp->rdata.name[rrp->rdlength-1] = '\0';
- /* replace first dot with @ if there is no @ */
- if(strchr(rrp->rdata.name,'@') == NULLCHAR
- && (p = strchr(rrp->rdata.name,'.')) !=
- NULLCHAR)
- *p = '@';
- if(strchr(rrp->rdata.name,'@') != NULLCHAR)
- tp = addlist(head,rrp->rdata.name,
- DOMAIN);
- else
- tp = addlist(head,rrp->rdata.name,
- LOCAL);
- ++inalias;
- }
- rrp = rrp->next;
- }
- free_rr(rrlp);
- if(inalias)
- return tp;
- else
- return addlist(head, user, LOCAL);
- }
-
- while (fgets(buf,LINELEN,fp) != NULLCHAR)
- {
- p = buf;
- if ( *p == '#' || *p == '\0')
- continue;
- rip(p);
-
- /* if not in an matching entry skip continuation lines */
- if (!inalias && isspace(*p))
- continue;
-
- /* when processing an active alias check for a continuation */
- if (inalias)
- {
- if (!isspace(*p))
- break; /* done */
- }
- else
- {
- s = p;
- SKIPWORD(p);
- *p++ = '\0'; /* end the alias name */
- SKIPSPACE (p);
- if (fail == NO_FAIL && strcmp(s, "default") == 0)
- if (strncmp(p, "deliver", 3) == 0)
- fail = FAIL_DELIVER;
- else {
- fail = FAIL_DEFAULT;
- strcpy(default_address, p);
- }
- if (strcmp(s,user) != 0)
- continue; /* no match go on */
- inalias = 1;
- }
-
- /* process the recipients on the alias line */
- SKIPSPACE(p);
- while(*p != '\0' && *p != '#')
- {
- s = p;
- SKIPWORD(p);
- if (*p != '\0')
- *p++ = '\0';
-
- /* find hostname */
- if (strchr(s,'@') != NULLCHAR)
- tp = addlist(head,s,DOMAIN);
- else
- tp = addlist(head,s,LOCAL);
- SKIPSPACE(p);
- }
- }
- (void) fclose(fp);
-
- if (inalias) /* found and processed and alias. */
- return tp;
-
- /* no alias found treat as a local address */
- return addlist(head, user, LOCAL);
- }
-
- #if defined(ANSIPROTO)
- static void
- smtplog(char *fmt, ...)
- {
- va_list ap;
- char *cp;
- long t;
- FILE *fp;
-
- if ((fp = fopen(Maillog,APPEND_TEXT)) == NULLFILE)
- return;
- time(&t);
- cp = ctime(&t);
- rip(cp);
- fprintf(fp,"%s ",cp);
- va_start(ap,fmt);
- vfprintf(fp,fmt,ap);
- va_end(ap);
- fprintf(fp,"\n");
- fclose(fp);
- }
-
- #else
-
- static void
- smtplog(fmt,arg1,arg2,arg3,arg4)
- char *fmt;
- int arg1,arg2,arg3,arg4;
- {
- char *cp;
- long t;
- FILE *fp;
-
- if ((fp = fopen(Maillog,APPEND_TEXT)) == NULLFILE)
- return;
- time(&t);
- cp = ctime(&t);
- rip(cp);
- fprintf(fp,"%s ",cp);
- fprintf(fp,fmt,arg1,arg2,arg3,arg4);
- fprintf(fp,"\n");
- fclose(fp);
- }
- #endif
-
- /* send mail to a single user. Can be called from the ax24 mailbox or
- ** from the return mail function in the smtp client
- */
- static int
- mailuser(data,from,to)
- FILE *data;
- char *from;
- char *to;
- {
-
- int address_type, ret;
- struct list *tolist = NULLLIST;
-
- /* check if address is ok */
- if ((address_type = validate_address(to)) == BADADDR)
- {
- return 1;
- }
- /* if a local address check for an alias */
- if (address_type == LOCAL)
- expandalias(&tolist, to);
- else
- /* a remote address is added to the list */
- addlist(&tolist, to, address_type);
- ret = mailit(data,from,tolist);
- del_list(tolist);
- return ret;
-
- }
-
- /* Mailer daemon return mail mechanism */
- int
- mdaemon(data,to,lp,bounce)
- FILE *data; /* pointer to rewound data file */
- char *to; /* Overridden by Errors-To: line if bounce is true */
- struct list *lp; /* error log for failed mail */
- int bounce; /* True for failed mail, otherwise return receipt */
- {
- time_t t;
- FILE *tfile;
- char buf[LINELEN], *cp, *newto = NULLCHAR;
- int cnt;
- if(to == NULLCHAR || (to != NULLCHAR && *to == '\0') || bounce)
- {
- while(fgets(buf,sizeof(buf),data) != NULLCHAR)
- {
- if(buf[0] == '\n')
- break;
- /* Look for Errors-To: */
- if(htype(buf) == ERRORSTO &&
- (cp = getaddress(buf,0)) != NULLCHAR)
- {
- free(newto);
- newto = strdup(cp);
- break;
- }
- }
- if(newto == NULLCHAR && ((to != NULLCHAR && *to == '\0') ||
- to == NULLCHAR))
- return -1;
- rewind(data);
- }
- if((tfile = tmpfile()) == NULLFILE)
- return -1;
- time(&t);
- fprintf(tfile,"%s%s",Hdrs[DATE],ptime(&t));
- fprintf(tfile,"%s<%ld@%s>\n",Hdrs[MSGID],get_msgid(),Hostname);
- fprintf(tfile,"%sMAILER-DAEMON@%s (Mail Delivery Subsystem)\n",
- Hdrs[FROM],Hostname);
- fprintf(tfile,"%s%s\n",Hdrs[TO],newto != NULLCHAR ? newto : to);
- fprintf(tfile,"%s%s\n\n",Hdrs[SUBJECT],
- bounce ? "Failed mail" : "Return receipt");
- if(bounce)
- {
- fprintf(tfile," ===== transcript follows =====\n\n");
- for (; lp != NULLLIST; lp = lp->next)
- fprintf(tfile,"%s\n",lp->val);
- fprintf(tfile,"\n");
- }
- fprintf(tfile," ===== %s follows ====\n",
- bounce ? "Unsent message" : "Message header");
-
- while(fgets(buf,sizeof(buf),data) != NULLCHAR)
- {
- if(buf[0] == '\n')
- break;
- fputs(buf,tfile);
- }
- if(bounce)
- {
- fputc('\n',tfile);
- while((cnt = fread(buf,1,sizeof(buf),data)) > 0)
- fwrite(buf,1,cnt,tfile);
- }
- fseek(tfile,0L,0);
-
- /* A null From<> so no looping replys to MAIL-DAEMONS */
-
- sprintf(buf, "postmaster@%s", Hostname);
- mailuser(tfile, buf, newto != NULLCHAR ? newto : to);
-
- fclose(tfile);
- free(newto);
- return 0;
- }
-
- /* Check to see that we are accepting mail from this sender. If we aren't
- * then tell 'em that mail from them is Unwanted. I added this facility to
- * allow auto-rejection of messages from auto-reply daemons. G7LEU
- */
- static int
- validate_sender(sender)
- char *sender;
- {
- FILE *fp;
- char buf[LINELEN + 1], *p;
- if ((fp = fopen(Mailkill, READ_TEXT)) == NULLFILE)
- /* Can't open SMTP reject file so let 'em through */
- return 1;
- while (fgets(buf,LINELEN,fp) != NULLCHAR)
- {
- p = buf;
- if (*p == '#' || *p == '\0')
- continue;
- rip(p);
- if (!stricmp(p, sender))
- {
- extern int smtpverbose;
- if (smtpverbose)
- tprintf("SMTP: Rejected mail from '%s'\n", sender);
- fclose(fp);
- return 0;
- }
- }
- fclose(fp);
- /* Sender OK */
- return 1;
- }
-
-
- /* Check to see if a local user actually exists - use the FTPUSERS file
- * Returns 0 if the user doesn't exist, 1 otherwise
- */
- static int
- validate_user(user)
- char *user;
- {
- char *cp;
-
- /* always allow 'postmaster' */
- if (stricmp(user,"postmaster")==0)
- {
- return 1;
- }
- /* otherwise check the ftpusers file */
-
- # ifdef NOVELL
- if(novell_start)
- {
- if(!get_mail_id(user,novell_server_name))
- {
- return 0;
- }
- }
- else
- # endif
- {
- if ((cp=userlookup(user,NULL,NULLCHARP,NULLINT,NULL)) == NULLCHAR)
- {
- return 0;
- }
- free(cp);
- }
- return 1;
- }
-